package cn.iocoder.yudao.module.agent.service.brokeragerecord;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.module.agent.dal.dataobject.brokerageuser.AgentBrokerageUserDO;
import cn.iocoder.yudao.module.agent.dal.dataobject.user.AgentUserDO;
import cn.iocoder.yudao.module.agent.enums.AgentBrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.agent.enums.AgentBrokerageRecordStatusEnum;
import cn.iocoder.yudao.module.agent.service.bo.borkeragerecord.BorkerageRecordAddReqBO;
import cn.iocoder.yudao.module.agent.service.brokerageuser.AgentBrokerageUserService;
import cn.iocoder.yudao.module.agent.service.user.AgentUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.validation.Valid;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.time.LocalDateTime;
import java.util.*;
import cn.iocoder.yudao.module.agent.controller.admin.brokeragerecord.vo.*;
import cn.iocoder.yudao.module.agent.dal.dataobject.brokeragerecord.AgentBrokerageRecordDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;

import cn.iocoder.yudao.module.agent.convert.brokeragerecord.AgentBrokerageRecordConvert;
import cn.iocoder.yudao.module.agent.dal.mysql.brokeragerecord.AgentBrokerageRecordMapper;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.agent.enums.ErrorCodeConstants.*;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;

/**
 * 代理人佣金记录 Service 实现类
 *
 * @author 曹建伟
 */
@Slf4j
@Service
@Validated
public class AgentBrokerageRecordServiceImpl implements AgentBrokerageRecordService {

    @Autowired
    private AgentBrokerageRecordMapper agentBrokerageRecordMapper;

    @Resource
    private AgentUserService agentUserService;

    @Resource
    private AgentBrokerageUserService agentBrokerageUserService;

    @Override
    public Long createBrokerageRecord(AgentBrokerageRecordCreateReqVO createReqVO) {
        // 插入
        AgentBrokerageRecordDO brokerageRecord = AgentBrokerageRecordConvert.INSTANCE.convert(createReqVO);
        agentBrokerageRecordMapper.insert(brokerageRecord);
        // 返回
        return brokerageRecord.getId();
    }

    @Override
    public void updateBrokerageRecord(AgentBrokerageRecordUpdateReqVO updateReqVO) {
        // 校验存在
        validateBrokerageRecordExists(updateReqVO.getId());
        // 更新
        AgentBrokerageRecordDO updateObj = AgentBrokerageRecordConvert.INSTANCE.convert(updateReqVO);
        agentBrokerageRecordMapper.updateById(updateObj);
    }

    @Override
    public void deleteBrokerageRecord(Long id) {
        // 校验存在
        validateBrokerageRecordExists(id);
        // 删除
        agentBrokerageRecordMapper.deleteById(id);
    }

    private void validateBrokerageRecordExists(Long id) {
        if (agentBrokerageRecordMapper.selectById(id) == null) {
            throw exception(BROKERAGE_RECORD_NOT_EXISTS);
        }
    }

    @Override
    public AgentBrokerageRecordDO getBrokerageRecord(Long id) {
        return agentBrokerageRecordMapper.selectById(id);
    }

    @Override
    public List<AgentBrokerageRecordDO> getBrokerageRecordList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return ListUtil.empty();
        }
        return agentBrokerageRecordMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<AgentBrokerageRecordDO> getBrokerageRecordPage(AgentBrokerageRecordPageReqVO pageReqVO) {
        return agentBrokerageRecordMapper.selectPage(pageReqVO);
    }

    @Override
    public List<AgentBrokerageRecordDO> getBrokerageRecordList(AgentBrokerageRecordExportReqVO exportReqVO) {
        return agentBrokerageRecordMapper.selectList(exportReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addBrokerage(Long userId, AgentBrokerageRecordBizTypeEnum bizType, @Valid List<BorkerageRecordAddReqBO> list){
        //todo config
        Integer brokerageFrozenDays  = 3;

        // 1.1 处理冻结时间
        LocalDateTime unfreezeTime = null;
        if (brokerageFrozenDays != null && brokerageFrozenDays > 0) {
            unfreezeTime = LocalDateTime.now().plusDays(brokerageFrozenDays);
        }

        //1.2 获取代理人信息
        //分销人信息
        AgentBrokerageUserDO agentBrokerageUserDO = agentBrokerageUserService.getBrokerageUserByUserId(userId);
        if(agentBrokerageUserDO == null){
            throw exception(BROKERAGE_USER_NOT_EXISTS);
        }

        //代理人信息
        AgentUserDO agentUser = agentUserService.getByUserId(agentBrokerageUserDO.getAgentUserId());
        if(agentUser == null){
            throw exception(USER_NOT_EXISTS);
        }

        if(!agentUser.getEnabled()) {
            throw exception(AGENT_NOT_ENABLED);
        }

        // 1.3 计算分佣
        int totalBrokerage = 0;
        int totalPrice = 0;
        List<AgentBrokerageRecordDO> records = new ArrayList<>();
        for (BorkerageRecordAddReqBO item : list) {
            // 计算金额
            totalPrice += item.getPayPrice();
            int brokeragePrice = calculatePrice(item.getPayPrice(), agentUser.getBrokerageRate());
            if (brokeragePrice <= 0) {
                continue;
            }
            totalBrokerage += brokeragePrice;
            // 创建记录实体
            records.add(AgentBrokerageRecordConvert.INSTANCE.convert(agentBrokerageUserDO.getAgentUserId(),
                                                                    agentBrokerageUserDO.getUserId(),
                                                                    bizType,
                                                                    brokerageFrozenDays,
                                                                    unfreezeTime,
                                                                    brokeragePrice,
                                                                    item.getTitle(),
                                                                    item.getBizId()));

        }

        if (CollUtil.isEmpty(records)) {
            return;
        }

        // 1.4 保存佣金记录
        agentBrokerageRecordMapper.insertBatch(records);

        // 2. 更新用户佣金
        if (brokerageFrozenDays != null && brokerageFrozenDays > 0) { // 更新用户冻结佣金
            agentUserService.updateUserFrozenBrokerage(agentUser.getUserId(), totalBrokerage);
        } else { // 更新用户可用佣金
            agentUserService.updateUserBrokerage(agentUser.getUserId(), totalBrokerage);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addBrokerage(Long userId, AgentBrokerageRecordBizTypeEnum bizType, Long bizId, Integer brokerage, String title){
        // 1. 校验佣金余额
        AgentUserDO agentUserDO = agentUserService.getByUserId(userId);
        if(agentUserDO == null){
            throw exception(USER_NOT_EXISTS);
        }

        if(agentUserDO.getBrokerage() + brokerage < 0){
            throw exception(BROKERAGE_NOT_ENOUGH);
        }

        // 2. 更新佣金余额
        Boolean success = agentUserService.updateUserBrokerage(userId, brokerage);
        if (!success) {
            // 失败时，则抛出异常。只会出现扣减佣金时，余额不足的情况
            throw exception(BROKERAGE_NOT_ENOUGH);
        }

        // 3. 新增记录
        AgentBrokerageRecordDO recordDO = AgentBrokerageRecordConvert.INSTANCE.convert(agentUserDO.getUserId(), agentUserDO.getUserId(), bizType, 0, null, brokerage, title, bizId);
        agentBrokerageRecordMapper.insert(recordDO);
    }

    /**
     * 计算佣金
     *
     * @param basePrice  佣金基数
     * @param percent    佣金比例
     * @return 佣金
     */
    int calculatePrice(Integer basePrice, Integer percent) {

        // 根据比例计算佣金
        if (basePrice != null && basePrice > 0 && percent != null && percent > 0) {
            return MoneyUtils.calculateRatePriceFloor(basePrice, Double.valueOf(percent));
        }
        return 0;
    }

    /**
     * 解冻单条佣金记录
     *
     * @param record 佣金记录
     * @return 解冻是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean unfreezeRecord(AgentBrokerageRecordDO record) {
        // 更新记录状态
        AgentBrokerageRecordDO updateObj = new AgentBrokerageRecordDO()
                .setStatus(AgentBrokerageRecordStatusEnum.SETTLEMENT.getStatus())
                .setUnfreezeTime(LocalDateTime.now());
        int updateRows = agentBrokerageRecordMapper.updateByIdAndStatus(record.getId(), record.getStatus(), updateObj);
        if (updateRows == 0) {
            log.error("[unfreezeRecord][record({}) 更新为已结算失败]", record.getId());
            return false;
        }

        // 更新用户冻结佣金
        agentUserService.updateFrozenPriceDecrAndPriceIncr(record.getAgentUserId(), -record.getPrice());
        log.info("[unfreezeRecord][record({}) 更新为已结算成功]", record.getId());
        return true;
    }

    @Override
    public int unfreezeRecord(){
        // 1. 查询待结算的佣金记录
        List<AgentBrokerageRecordDO> records = agentBrokerageRecordMapper.selectListByStatusAndUnfreezeTimeLt(
                AgentBrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus(), LocalDateTime.now());
        if (CollUtil.isEmpty(records)) {
            return 0;
        }

        // 2. 遍历执行
        int count = 0;
        for (AgentBrokerageRecordDO record : records) {
            try {
                boolean success = getSelf().unfreezeRecord(record);
                if (success) {
                    count++;
                }
            } catch (Exception e) {
                log.error("[unfreezeRecord][record({}) 更新为已结算失败]", record.getId(), e);
            }
        }
        return count;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelBrokerage(Long agentUserId, Long sourceUserId, Long bizId){
        AgentBrokerageRecordDO recordDO = agentBrokerageRecordMapper.selectByBizIdAndUserId(bizId, sourceUserId);
        if(recordDO == null){
            log.error("[cancelAgentBrokerage][sourceUserId({})][bizId({}) 更新为已失效失败：记录不存在]", sourceUserId, bizId);
            return;
        }

        // 1. 更新佣金记录为已失效
        AgentBrokerageRecordDO updateObj = new AgentBrokerageRecordDO().setStatus(AgentBrokerageRecordStatusEnum.CANCEL.getStatus());
        int updateRows = agentBrokerageRecordMapper.updateByIdAndStatus(recordDO.getId(), recordDO.getStatus(), updateObj);
        if (updateRows == 0) {
            log.error("[cancelBrokerage][record({}) 更新为已失效失败]", recordDO.getId());
            return;
        }

        // 2. 更新用户的佣金
        if (AgentBrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus().equals(recordDO.getStatus())) {
            agentUserService.updateUserFrozenBrokerage(agentUserId, -recordDO.getPrice());
        } else if (AgentBrokerageRecordStatusEnum.SETTLEMENT.getStatus().equals(recordDO.getStatus())) {
            agentUserService.updateUserBrokerage(agentUserId, -recordDO.getPrice());
        }
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private AgentBrokerageRecordServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }
}
